/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ui.gui.documents;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swingx.CodeEditor;

import model.AltaRicaNode;

import parser.AltaRicaParseModel;
import parser.AltaRicaParser;
import parser.AltaRicaParseNode.CycleDetectionException;
import ui.WindowManager;
import ui.gui.dialogs.AddSubNodeDialog;
import ui.gui.dialogs.RefactorRenameDialog;
import ui.gui.documents.actions.ActionAddSubNode;
import ui.gui.documents.actions.ActionRefreshGraph;
import ui.gui.documents.actions.ActionRefactorNode;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.handler.mxKeyboardHandler;
import com.mxgraph.swing.handler.mxRubberband;
import com.mxgraph.swing.util.mxMorphing;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import common.AltaRicaModel;

/**
 * 
 * @author stephane
 */
public class AltaRicaTree extends AbstractDocument {

	private static final long serialVersionUID = 1L;

	private BorderLayout _layout = new BorderLayout();

	private AbstractDocument _document = null;

	private mxGraph _graph = null;

	private mxGraphComponent _graphComponent = null;

	private AltaRicaModel _ast = null;

	private ArrayList<Object> _nodes = new ArrayList<Object>();

	private Object _rootNode = null;

	private AltaRicaNode _altaricaRootNode = null;

	private JPopupMenu _popupMenu = new JPopupMenu();

	private JComboBox _listNode = new JComboBox();

	private void buildMenus(boolean onlyRefresh) {

		_popupMenu.removeAll();
		clearAvailableAction();

		if (!onlyRefresh) {
			addAction(new ActionRefactorNode(this));
			addAction(new ActionAddSubNode(this));
		}
		for (EditorAction action : getAvailableActions()) {
			_popupMenu.add(action);
		}
	}

	@Override
	public void initComponents() {
		
	}
	public void addSubNode() {
		if (_graph.getSelectionCell() != _rootNode && _ast != null) {
			com.mxgraph.model.mxCell cell = (com.mxgraph.model.mxCell) _graph
					.getSelectionCell();
			AltaRicaNode node = _ast.getNode(cell.getValue().toString());
			if (node != null) {
				new AddSubNodeDialog(WindowManager.getInstance(), node, _ast,
						true).setVisible(true);
				AltaricaCodeEditor editor = (AltaricaCodeEditor) _document;
				editor.setText(_ast.string());
				editor.setIsDirty(true);
				refreshGraph();
			}
		} else if (_graph.getSelectionCell() == _rootNode && _ast != null) {
			new AddSubNodeDialog(WindowManager.getInstance(), null, _ast, true)
					.setVisible(true);
			AltaricaCodeEditor editor = (AltaricaCodeEditor) _document;
			editor.setText(_ast.string());
			editor.setIsDirty(true);
			refreshGraph();
		}
	}

	public void refactorRename() {
		if (_graph.getSelectionCell() != _rootNode && _ast != null) {
			com.mxgraph.model.mxCell cell = (com.mxgraph.model.mxCell) _graph
					.getSelectionCell();
			AltaRicaNode node = _ast.getNode(cell.getValue().toString());
			if (node != null) {
				new RefactorRenameDialog(WindowManager.getInstance(), node,
						true).setVisible(true);
				AltaricaCodeEditor editor = (AltaricaCodeEditor) _document;
				editor.setText(_ast.string());
				editor.setIsDirty(true);
				refreshGraph();
			}
		}
	}

	public AltaRicaTree(AbstractDocument document) {

		this._document = document;
		this._layout = new BorderLayout();
		this.setLayout(this._layout);
		((AltaricaCodeEditor) this._document).setTreeView(this);
		this.setFile(document.getFile());
		init();

		this._rootNode = _graph.insertVertex(_graph.getDefaultParent(), null,
				_document.getFileName(), 20, 20, 80, 30);
		addAction(new ActionRefreshGraph(this));
		refreshGraph();
	}

	public void changeRoot() {
		_graph.removeCells(_graph.getChildVertices(_graph.getDefaultParent()));
		if (this._listNode.getSelectedItem() != null) {
			this._altaricaRootNode = _ast.getNode(this._listNode
					.getSelectedItem().toString());
			this._rootNode = _graph.insertVertex(_graph.getDefaultParent(),
					null, this._listNode.getSelectedItem().toString(), 20, 20,
					80, 30);
		}
		BeginNodeUpdate();
	}

	private void BeginNodeUpdate() {
		if (this._ast != null) {
			try {
				_graph.getModel().beginUpdate();
				drawNodes(_altaricaRootNode, _rootNode);
			} finally {
				_graph.getModel().endUpdate();
			}

			this._graph.setMinimumGraphSize(this._graph
					.getBoundingBoxFromGeometry(this._nodes.toArray()));
		}
	}

	public void refreshGraph() {
		loadAst();
		BeginNodeUpdate();

	}

	private void drawNodes(AltaRicaNode anode, Object parentNode) {
		_nodes.clear();
		_nodes.add(_rootNode);
		Object parent = _graph.getDefaultParent();
		_graph.getModel().beginUpdate();

		if (_ast != null) {
			if (anode == null) {
				for (AltaRicaNode node : _ast.getNodes()) {

					Object v1 = _graph.insertVertex(parent, node.getName(),
							node.getName(), 20, 20, 80, 30);

					_graph.insertEdge(parent, null, "", parentNode, v1);

					if (!node.getSubs().isEmpty()) {
						drawNodes(node, v1);
					}
					_nodes.add(v1);

				}
			} else {
				for (String nodeName : anode.getSubs()) {
					AltaRicaNode node = anode.getSub(nodeName);
					if (node != null) {

						Object v1 = _graph.insertVertex(parent, node.getName(),
								nodeName, 20, 20, 80, 30);

						_graph.insertEdge(parent, null, node.getName(),
								parentNode, v1);

						if (!node.getSubs().isEmpty()) {
							drawNodes(node, v1);
						}
						_nodes.add(v1);
					}
				}
			}
		}
		layoutGraph();
	}

	private void layoutGraph() {

		_graph.getModel().beginUpdate();
		mxHierarchicalLayout layout = new mxHierarchicalLayout(_graph);
		layout.setInterHierarchySpacing(50);
		layout.setInterRankCellSpacing(50);
		_graph.getModel().beginUpdate();
		try {

			layout.execute(null, _nodes);
			_graph.repaint();
			_graph.refresh();
		} finally {
			mxMorphing morph = new mxMorphing(_graphComponent, 20, 1.2, 20);
			morph.startAnimation();
		}
		_graph.getModel().endUpdate();
		_graph.getView().setTranslate(new mxPoint(50, 50));
		_graphComponent.setZoomPolicy(mxGraphComponent.ZOOM_POLICY_WIDTH);
	}

	private void loadAst() {
		String lastSelection = "";

		if (this._listNode.getSelectedItem() != null) {
			lastSelection = this._listNode.getSelectedItem().toString();
		}

		this._listNode.removeAllItems();

		AltaricaCodeEditor editor = (AltaricaCodeEditor) this._document;
		InputStream is = new ByteArrayInputStream(editor.getText().getBytes());
		AltaRicaParseModel pmodel = AltaRicaParser.parseModel(editor
				.getFileName(), is);
		try {
			this._ast = AltaRicaModel.compileFromAST(pmodel, false);
		} catch (CycleDetectionException exception) {
			System.out.println(exception.getMessage());
			this._ast = null;
		}

		_graph.removeCells(_graph.getChildVertices(_graph.getDefaultParent()));
		if (this._ast != null) {
			for (AltaRicaNode node : _ast.getNodes()) {
				this._listNode.addItem(node.getName());
			}
			if (lastSelection != "") {

				for (int i = 0; i < this._ast.getNodes().size(); i++) {
					if (this._listNode.getItemAt(i).equals(lastSelection)) {
						this._listNode.setSelectedIndex(i);
					}
				}
			}
			if (this._listNode.getSelectedItem() != null ) {
				this._altaricaRootNode = _ast.getNode(this._listNode
						.getSelectedItem().toString());
				this._rootNode = _graph.insertVertex(_graph.getDefaultParent(),
						null, this._listNode.getSelectedItem().toString(), 20, 20,
						80, 30);
			}
		}
	}

	public void init() {
		JPanel headerPanel = new JPanel(new BorderLayout());
		this._graph = new mxGraph();
		this._graphComponent = new mxGraphComponent(_graph);
		this._graph.setAllowDanglingEdges(false);
		this._graph.setAutoSizeCells(true);
		this._graphComponent.setConnectable(false);
		this._graphComponent.setToolTips(true);
		this._graphComponent.setFoldingEnabled(true);
		this._graphComponent.setEnterStopsCellEditing(true);
		this._graph.setCellsEditable(false);
		this._graph.setCellsMovable(false);
		this._graph.setCellsDisconnectable(false);
		headerPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
		this.add(headerPanel, BorderLayout.NORTH);
		headerPanel.add(this._listNode, BorderLayout.CENTER);
		headerPanel.add(new JLabel("Node : "), BorderLayout.WEST);
		new mxRubberband(_graphComponent);
		new mxKeyboardHandler(_graphComponent);

		this._graph.setMinimumGraphSize(new mxRectangle(0, 0, 65535, 65535));
		this._graphComponent.setGraph(_graph);

		this.add(_graphComponent, BorderLayout.CENTER);

		_graphComponent.getGraphControl().addMouseListener(new MouseAdapter() {

			public void mouseReleased(MouseEvent e) {
				Object cell = _graphComponent.getCellAt(e.getX(), e.getY());

				buildMenus(!(cell != null && e.getButton() == MouseEvent.BUTTON3));

				if (e.getButton() == MouseEvent.BUTTON3) {
					_popupMenu.show(e.getComponent(), e.getX(), e.getY());
				}
				if (e.getClickCount() == 2 && cell != null) {
					mxCell cellule = (mxCell) cell;
					String nodeName = "";
					if (cellule.getEdgeAt(0) != null) {
						// subnode
						nodeName = cellule.getEdgeAt(0).getValue().toString();
					} else {
						// node
						nodeName = cellule.getValue().toString();
					}
					AltaRicaNode node = _ast.getNode(nodeName);
					if (node != null) {
						((AltaricaCodeEditor) _document).gotoLine(node
								.getLine());
						WindowManager.getInstance().SelectWindow(_document);
					}
				}
			}
		});

		this._listNode.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				changeRoot();
			}
		});

	}

	public String getFileName() {
		return super.getFileName() + "- [Design]";
	}

	@Override
	public void OnLoad() throws Exception {

	}

	@Override
	public void OnSave() throws Exception {

	}


	@Override
	public ImageIcon getIcon() {
		return new javax.swing.ImageIcon(getClass().getResource(
				"/resources/altaricaIcon.png"));
	}

	public CodeEditor getEditorObject() {
		return null;
	}

	public void refresh() {
		refreshGraph();
	}

}
